//引入对应的包
//koa-router 写接口用的 
import Router from 'koa-router';
//将用户登录信息 比如 code 等存储在redis中 
import Redis from 'koa-redis'
//nodeMailer 使用nodejs发送邮件时候的必备应用程序 引入
import nodeMailer from 'nodemailer'
//user 数据表
import User from '../dbs/models/users'
//passport 登录验证的配置
import Passport from './utils/passport'
//引入 关于smtp的配置主要是dbs中的smtp配置
import Email from '../dbs/config'
import axios from './utils/axios'


//创建一个新的接口 前缀为users
let router = new Router({
  prefix: '/users'
})

//创建一个redis存储的客户机
let Store = new Redis().client

//注册接口
router.post('/signup', async (ctx) => {
  const {
    username,
    password,
    email,
    code
  } = ctx.request.body;

  if (code) {
    //从redis 中拿出发送时候的验证码和保存时间 nodemail${username} 设置时候规定的名称
    const saveCode = await Store.hget(`nodemail:${username}`, 'code')
    const saveExpire = await Store.hget(`nodemail:${username}`, 'expire')
    if (code === saveCode) {
      if (new Date().getTime() - saveExpire > 0) {
        ctx.body = {
          code: -1,
          msg: '验证码已过期，请重新尝试'
        }
        return false
      }
    } else {
      ctx.body = {
        code: -1,
        msg: '请填写正确的验证码'
      }
    }
  } else {
    ctx.body = {
      code: -1,
      msg: '请填写验证码'
    }
  }
  //从数据空拿出 如果存在 return
  let user = await User.find({
    username
  })
  if (user.length) {
    ctx.body = {
      code: -1,
      msg: '已被注册'
    }
    return
  }
  //满足以上条件 在数据表中新建一条数据
  let nuser = await User.create({
    username,
    password,
    email
  })
  if (nuser) {
    //新建成功 返回不为空 请求登录接口
    let res = await axios.post('/users/signin', {
      username,
      password
    })
    if (res.data && res.data.code === 0) {
      ctx.body = {
        code: 0,
        msg: '注册成功',
        user: res.data.user
      }
    } else {
      ctx.body = {
        code: -1,
        msg: 'error'
      }
    }
  } else {
    ctx.body = {
      code: -1,
      msg: '注册失败'
    }
  }
})
//登录接口 这里使用了possport 所以直接使用possport固定的pai 返回authenticate()使用我们之前定义的本地策略
router.post('/signin', async (ctx, next) => {
  //这个方法结束一定要调用(ctx,next)
  return Passport.authenticate('local', function (err, user, info) {
    //两个参数，第一个为策略 第二个是回调 回调中如果验证失败。err存在
    //info为具体信息 user为登录的对象。user存在就是成功
    // 这里使用的策略是我们之前为passport定义的策略
    // 以及这里回调的参数 同时也等同于 配置策略时候done的参数

    //登录接口就相当于进入了passport的使用策略的验证 等使用策略验证完将信息传递给登录的Passport.authenticate这个回调
    // 这个回调通过参数来判断是否可以成功登陆
    //如果满足条件 user存在且error不存在 可以登录则调用return ctx.login(user) 返回调用login传入当前用户信息
    //然后login执行的时候会进行序列化和反序列化的配置(login源码中封装好了的)
    //之后序列化将当前登陆信息保存在session.passport中
    //logout（）方法同理  也就是退出登录 同时清除session.passport中当前用户的信息
    if (err) {
      ctx.body = {
        code: -1,
        msg: err
      }
    } else {
      if (user) {
        ctx.body = {
          code: 0,
          msg: '登录成功',
          user
        }
        //调用passport的login()  login的源码中就会进行关于序列化配置(passport上存在这个属性)
        return ctx.login(user)
      } else {
        ctx.body = {
          code: 1,
          msg: info
        }
      }
    }
    //关于这里记住调用 ctx,next 交给下一个中间件  登录接口调用authenticate一定要记住这步
  })(ctx, next)
})
//验证码接口
router.post('/verify', async (ctx, next) => {
  let username = ctx.request.body.username
  const saveExpire = await Store.hget(`nodemail:${username}`, 'expire')
  if (saveExpire && new Date().getTime() - saveExpire < 0) {
    ctx.body = {
      code: -1,
      msg: '验证请求过于频繁，1分钟内1次'
    }
    return false
  }
  //定义发送的对象
  let transporter = nodeMailer.createTransport({
    service: 'qq', //配置使用的邮箱服务
    //验证用户
    auth: {
      user: Email.smtp.user,
      pass: Email.smtp.pass
    }
  })
  //定义接受的对象以及验证码和过期时间 之前我们都在smtp中配置过
  let ko = {
    code: Email.smtp.code(),
    expire: Email.smtp.expire(),
    email: ctx.request.body.email,
    user: ctx.request.body.username
  }
  //定义邮件的内容
  let mailOptions = {
    from: `"认证邮件" <${Email.smtp.user}>`,
    to: ko.email,
    subject: '王浩羽给你的注册码',
    html: `王浩羽给你的邀请码是${ko.code}`
  }
  //调用者调用发送的方法 sendMail
  //两个参数 第一个为错误 如果失败则error存在
  await transporter.sendMail(mailOptions, (error, info) => {
    if (error) {
      return console.log(error)
    } else {
      //当发送成功之后 在redis也就是store中存储对应的值
      //nodemail:${ko.user} 我们规定的字段 。
      //之后为键值对
      //拿的时候 hget(规定字段名, 需要的键)  ----就可以拿到对应的名
      Store.hmset(`nodemail:${ko.user}`, 'code', ko.code, 'expire', ko.expire, 'email', ko.email)
    }
  })
  //执行到这一步返回
  ctx.body = {
    code: 0,
    msg: '验证码已发送，可能会有延时，有效期1分钟'
  }
})
//退出的接口  ctx.isAuthenticated  官方api规定的方法 判断当前用户是否登录
router.get('/exit', async (ctx, next) => {
  //logout()和login() 一样 是passport自己规定的方法 登录/登出 只需要调用就好了
  await ctx.logout()
  if (!ctx.isAuthenticated()) {
    //如果没有登录 也就是成功退出 ctx.isAuthenticated为false
    ctx.body = {
      code: 0
    }
  } else {
    ctx.body = {
      code: -1
    }
  }
})
//获取用户信息的接口
router.get('/getUser', async (ctx) => {
  //如果当前用户已经登录
  //isAuthenticated 这个函数 判断session里的passport属性是否保存当前用户信息
  if (ctx.isAuthenticated()) {
    //这里因为我们关于possport的信息都存储在cookies中
    //cookies在server端都存在session中
    //所以使用ctx.session.passport获取session信息
    //passport中一定存在当前登录user对象

    //因为序列化把用户信息存储在了 session中名passport的字段中
    //所以可以直接获取
    const {
      username,
      email
    } = ctx.session.passport.user
    ctx.body = {
      user: username,
      email
    }
  } else {
    ctx.body = {
      user: '',
      email: ''
    }
  }
})

export default router
